package bootstrap

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"logbeat/internal/conf"
	"logbeat/internal/db"
	"logbeat/internal/globalvar"
	"logbeat/internal/req"
	"logbeat/internal/textop"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/hpcloud/tail"
	"github.com/panjf2000/ants"
	"go.uber.org/zap"
)

func Start(config *conf.Config) {
	log.Println("开始处理函数")
	//	启动一个协程去处理文本信息
	go textop.ListenTextChan()
	// 初始化目标目录map
	initTargetDirFile(config.DirInfo)

	// 启动一个协程去监控目标目录下是否有创建新的文件, 15秒执行一次新文件检查
	go func() {
		for range globalvar.Ticker.C {
			log.Println("开始检查目录")
			checkTargetDirFile(config.DirInfo)
		}
	}()

	// 创建一个定时任务，每天执行一次， 删除旧的索引
	go func() {
		targetTime := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 2, 0, 0, 0, time.Local)

		ticker := time.NewTicker(24 * time.Hour) // 每隔 24 小时触发一次
		defer ticker.Stop()
		for {
			now := time.Now()
			if now.After(targetTime) { // 如果当前时间已经超过了目标时间
				targetTime = targetTime.AddDate(0, 0, 1) // 更新目标时间为第二天的同一时间
			}
			timeToWait := targetTime.Sub(now) // 计算距离下一次执行还有多久
			select {
			case <-ticker.C: // 每隔 24 小时检查一次
				continue
			case <-time.After(timeToWait): // 当达到目标时间时
				DailyTask() // 执行每日任务
			}
		}
	}()
}

func Clean(targetDir string, logDirStruct globalvar.LogDirStruct) {
	log.Println("开始清理")
	logDirStruct.AntsPool.Release()
	countLines, err := countLines(filepath.Join(logDirStruct.DirPath, logDirStruct.LogFileName))
	if err != nil {
		log.Println("获取文件行数失败", err)
	}
	// offset, err := skipLines(filepath.Join(targetDir, logDirStruct.NewLogFileName), countLines)
	// if err != nil {
	// 	log.Println("跳过文件行数失败", err)
	// }

	var logFileRecord db.LogFileRecord

	globalvar.DB.First(&logFileRecord, "file_name = ?", filepath.Join(targetDir, logDirStruct.LogFileName))
	if logFileRecord.ID == 0 {
		logFileRecord.FileName = filepath.Join(targetDir, logDirStruct.LogFileName)
		logFileRecord.Offset = countLines
		globalvar.DB.Create(&logFileRecord)
	} else {
		logFileRecord.Offset = countLines
		globalvar.DB.Save(&logFileRecord)
	}

}

func initTargetDirFile(targetDirInfoList []conf.ConfigDirInfo) {
	// 遍历目标目录
	for _, dirInfo := range targetDirInfoList {
		if dirInfo.DirPath == "" {
			log.Fatalln("目标目录路径不能为空")
		}
		if dirInfo.IndexPrefix == "" {
			log.Fatalln("目标目录路径索引前缀不能为空")
		}
		//获取目录下创建时间最近的文件
		newLogFile := getMostRecentFile(dirInfo.DirPath)
		// 为每个目标目录创建一个 协程池
		p, _ := ants.NewPool(1)
		val := globalvar.LogDirStruct{
			IndexPrefix: dirInfo.IndexPrefix,
			LogFileName: newLogFile,
			DirPath:     dirInfo.DirPath,
			AntsPool:    p,
		}
		// 由一个全局的map 管理所有的目标目录， 管理的目的是为了停止进程的时候， 知道需要清理什么
		globalvar.ChMap[dirInfo.DirPath] = val
		// 提交 协程池 任务
		p.Submit(func() {
			log.Println("提交任务路径", filepath.Join(dirInfo.DirPath, newLogFile))
			execLog(val)
		})
	}
}

/*
 * 当检查到目标目录有新的文件， 则需要更新目标目录的协程池， 并且提交新的任务
 */
func checkTargetDirFile(targetDirInfoList []conf.ConfigDirInfo) {
	for _, dirInfo := range targetDirInfoList {
		if dirInfo.DirPath == "" {
			log.Fatalln("目标目录路径不能为空")
		}
		if dirInfo.IndexPrefix == "" {
			log.Fatalln("目标目录路径索引前缀不能为空")
		}
		newLogFile := getMostRecentFile(dirInfo.DirPath)

		val := globalvar.ChMap[dirInfo.DirPath]
		if val.LogFileName != newLogFile {

			// 释放当前协程池
			val.AntsPool.Release()
			log.Println("已释放协程池任务", filepath.Join(val.DirPath, val.LogFileName))
			// 更新值
			val.LogFileName = newLogFile
			p, _ := ants.NewPool(1)
			val.AntsPool = p
			p.Submit(func() {
				log.Println("变更提交任务路径", dirInfo.DirPath, newLogFile)
				execLog(val)
			})
			globalvar.ChMap[dirInfo.DirPath] = val
		}
	}

}

/**
 * 获取目录下创建时间最近的文件
 */
func getMostRecentFile(directory string) string {
	files, err := os.ReadDir(directory)
	if err != nil {
		fmt.Printf("Error listing files in directory: %v\n", err)
		return ""
	}

	mostRecentFile := ""
	mostRecentTime := time.Now()

	for _, file := range files {
		filePath := filepath.Join(directory, file.Name())
		fileInfo, err := os.Stat(filePath)
		if err != nil {
			fmt.Printf("Error getting file info for '%s': %v\n", filePath, err)
			continue
		}

		// 检查文件名称如果为空， 则认为是第一次遍历， 所以直接赋值ModTime，和文件名
		if mostRecentFile == "" {
			mostRecentTime = fileInfo.ModTime()
			mostRecentFile = file.Name()
			continue
		} else {
			fileTime := fileInfo.ModTime()
			if mostRecentTime.Unix() < fileTime.Unix() {
				mostRecentTime = fileTime
				mostRecentFile = file.Name()
			}
		}

	}

	return mostRecentFile
}

func execLog(logDirStruct globalvar.LogDirStruct) {
	// 文件偏移量， 也就是偏移的行数
	offset := int64(0)

	var logFileRecord db.LogFileRecord
	fmt.Println(globalvar.DB)
	execLogFilePath := filepath.Join(logDirStruct.DirPath, logDirStruct.LogFileName)
	fmt.Println(execLogFilePath)

	globalvar.DB.First(&logFileRecord, "file_name = ?", execLogFilePath)
	if logFileRecord.ID == 0 {
		logFileRecord.FileName = execLogFilePath
		logFileRecord.Offset = 0
		globalvar.DB.Create(&logFileRecord)
	} else {
		offset, _ = skipLines(execLogFilePath, logFileRecord.Offset)
	}
	fmt.Println("offset", offset)
	// 从偏移量打开监听的文件
	tailObj, err := tail.TailFile(execLogFilePath, tail.Config{
		ReOpen:    true,
		Follow:    true,
		Location:  &tail.SeekInfo{Offset: offset, Whence: io.SeekCurrent},
		MustExist: false,
		Poll:      true,
	})
	if err != nil {
		log.Fatalln("启动失败:", zap.String("file", execLogFilePath), zap.Error(err))
	} else {
		log.Println("监听文件", zap.String("file", execLogFilePath))
	}
	// 开始监听打开的文件，当监听到就触发到for里面的内容
	for line := range tailObj.Lines {
		// line.Text 是文件新增行的内容
		fmt.Println(line.Text)
		// 下面是解析当前行内容，
		if strings.Contains(line.Text, "-日志：") {
			index := strings.Index(line.Text, "-日志：")
			if index != -1 {
				// 找到了符合标准内容的日志
				log.Println("监听到日志", zap.String("file", execLogFilePath), zap.String("line", line.Text))
				afterPrefix := line.Text[index+len("-日志："):]
				// 就将其写入到TxtChan通道种
				globalvar.TxtChan <- [2]string{logDirStruct.IndexPrefix + logDirStruct.LogFileName, afterPrefix}
			}
		} else {
			log.Println("不符合要求的日志")
		}

	}

}

func skipLines(filename string, n int) (int64, error) {
	file, err := os.Open(filename)
	if err != nil {
		return 0, err
	}
	defer file.Close()

	reader := bufio.NewReader(file)
	var offset int64
	for i := 0; i < n; i++ {
		_, err := reader.ReadBytes('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			return 0, err
		}
		offset, err = file.Seek(0, io.SeekCurrent)
		if err != nil {
			return 0, err
		}
	}
	return offset, nil
}

func countLines(filename string) (int, error) {
	// 读取文件内容
	content, err := os.ReadFile(filename)
	if err != nil {
		return 0, err
	}

	// 分割文件内容为多行
	lines := strings.Split(string(content), "\n")

	// 计算行数，注意末尾可能有一个空行
	lineCount := len(lines)
	if lines[lineCount-1] == "" {
		lineCount-- // 去除末尾的空行
	}

	return lineCount, nil
}

// 定义一个函数，代表每天需要执行的任务
// 这里可以添加你需要执行的具体任务代码
func DailyTask() {
	log.Println("开始删除相关索引")
	// 先获取所有的索引
	url := globalvar.SysConf.ZincsearchServer.Address + "/api/index_name"
	bd, status, _err := req.Request("GET", url, strings.NewReader(""))
	if _err != nil {
		log.Println("获取索引失败", zap.Error(_err))
		return
	}
	if status != 200 {
		log.Println("获取索引失败", zap.Int("status", status))
		return
	}

	now := time.Now()

	nDaysAgo := now.AddDate(0, 0, -globalvar.SysConf.MaxHistory)

	// 格式化日期为 "2006-01-02" 的格式
	formatted := nDaysAgo.Format("2006-01-02")

	// 输出格式化后的日期
	log.Println("删除目标日期索引" + formatted)
	minIndexs := []string{}
	for _, dirInfoItem := range globalvar.SysConf.DirInfo {
		minIndexs = append(minIndexs, dirInfoItem.IndexPrefix+"Log-"+formatted)

	}
	log.Println(minIndexs)

	bd = bd[1 : len(bd)-1]
	indexs := strings.Split(bd, `","`)

	for _, indexItem := range indexs {
		// 去除每个元素两端的双引号
		indexItem = strings.Trim(indexItem, `"`)
		indexItemSplit := strings.Split(indexItem, ".")
		for _, minIndex := range minIndexs {
			if indexItemSplit[0] < minIndex {
				log.Println("删除索引", indexItem)
				delurl := globalvar.SysConf.ZincsearchServer.Address + "/api/index/" + indexItem
				req.Request("DELETE", delurl, strings.NewReader(""))
			}
		}
	}
}
